package bev.article;

import java.sql.Timestamp;

import java.util.Date;

import java.awt.Component;
import java.awt.Insets;
import java.lang.String;

import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.JTextField;
import javax.swing.BoxLayout;
import javax.swing.Box;

import bev.HelperUtility;

/**
 * The abstract class all article type classes stem from. Contains common data
 * and logic used by all derived article classes and provides standardized
 * abstract methods all article classes must implement.
 */
public abstract class BaseArticle {
    // Common article data.
    protected int id_num;
    protected String name;
    protected Timestamp timeCreated;
    protected Timestamp timeModified;

    // Text fields for editing article data in 'Edit Mode'.
    JTextField txtName;

    // Stores the last invalid field message for user. This is populated each
    // time a user attempts to save changes to an article and one of their
    // edit fields contains invalid data (e.g. text in a number field).
    private String invalidFieldMsg;

    /**
     * Implements common constructor logic for all article classes.
     */
    public BaseArticle() {
        id_num = -1;
        name = "Untitled";
        timeModified = timeCreated = new Timestamp( ( new Date() ).getTime() );
    }

    /**
     * @return The article ID that identifies this article in the database.
     */
    public int getID() {
        return id_num;
    }

    /**
     * @return The name of this article (e.g. the name of a person, place,
     *         event, etc.).
     */
    public String getName() {
        return name;
    }

    /**
     * Returns a string containing a message about which field failed validation
     * when calling saveEditData() (if a field was invalid and saveEditData()
     * returned false).
     */
    public String getInvalidFieldMsg() {
        return invalidFieldMsg;
    }

    @Override
    public String toString() {
        return String.format( "%d", id_num );
    }

    // Abstract methods.
    /**
     * Loads this article object with data from an article in the database. The
     * article is identified both by the article type of this class and the
     * provided article ID.
     * 
     * @param id
     *            The article ID identifying the article to load from the
     *            database.
     */
    public abstract void loadArticle( int id );

    /**
     * Stores the article data contained in this object back to the database.
     * Creates a new article if no matching article is found.
     */
    public abstract void storeArticle();

    /**
     * Saves the user data currently sitting in the article edit fields from
     * 'Edit Mode' to this article object. Specifically the data from the edit
     * fields custom to this article type (person, place, event, etc.). The
     * saving of data in edit fields common to all article types is handled
     * elsewhere.
     */
    protected abstract void saveCustomEditData();

    /**
     * Adds the main article body text to the text pane passed in. The article
     * body is the part of the article custom to each article type (the header
     * and footer on the other hand are the same for all article types).
     * 
     * @param textPane
     *            The text pane containing the main article text that the
     *            article body will be added to.
     */
    protected abstract void addBodyText( JTextPane textPane );

    /**
     * @return A JPanel containing the edit fields used to edit the article
     *         content in the article body (the part of the article custom to
     *         each article type). This JPanel is displayed in 'Edit Mode',
     *         along with the JPanels for editing other article parts (such as
     *         the header and footer).
     */
    protected abstract JPanel getBodyEditControls();

    /**
     * Validates the user data currently entered in the article edit fields in
     * the article body. This is called before the data is saved from the
     * article body edit fields to the article object itself. If validation
     * fails, a message to the user about which field failed should be stored in
     * invalidFieldMsg, which can be retried via a call to getInvalidFieldMsg().
     * 
     * @return True if all of the fields in the article body contain valid data
     *         and false if any fields contain data that must be changed before
     *         the data can be saved to the article object.
     */
    protected abstract boolean validateBodyEditFields();

    /**
     * Adds the article header text to the text pane passed in. The article
     * header is common to all article types.
     * 
     * @param textPane
     *            The text pane containing the main article text that the
     *            article header will be added to.
     */
    private void addHeaderText( JTextPane textPane ) {
        // Article title.
        ArticleFormatter.addMedHeader( textPane, name );
        ArticleFormatter.addNewLine( textPane, 2 );
    }

    /**
     * Adds the article footer text to the text pane passed in. The article
     * footer is common to all article types.
     * 
     * @param textPane
     *            The text pane containing the main article text that the
     *            article footer will be added to.
     */
    private void addFooterText( JTextPane textPane ) {
        ArticleFormatter.addNewLine( textPane, 6 );

        // Add created and modified dates + times.
        ArticleFormatter.addRegular( textPane,
                String.format( "Created:            %s\nLast modified:    %s",  // Format string.
                        HelperUtility.formatTimeStamp( timeCreated ),           // Created timestamp.
                        HelperUtility.formatTimeStamp( timeModified ) )         // Modified timestamp.
                );
    }

    /**
     * @return A text pane containing a rendered text version of this article to
     *         be displayed in 'View Mode'.
     */
    public JTextPane getTextPane() {
        JTextPane textPane = new JTextPane();
        textPane.setMargin( new Insets( 10, 10, 10, 10 ) );
        ArticleFormatter.setTabSize( textPane, 4, 50 );

        // Article header.
        addHeaderText( textPane );

        // Article body.
        addBodyText( textPane );

        // Article footer.
        addFooterText( textPane );

        return textPane;
    }

    /**
     * @return A JPanel containing the edit fields used to edit the article
     *         content in the article header. This JPanel is displayed in 'Edit
     *         Mode', along with the JPanels for editing other article parts
     *         (such as the body and footer).
     */
    private JPanel getHeaderEditControls() {
        ArticleEditPanel panel = new ArticleEditPanel();

        // Set up fields.
        txtName = new JTextField( name );

        // Add fields.
        panel.addComponent( "Name:", txtName,
                ArticleEditPanel.getMediumFieldSize() );
        panel.commitComponents();

        return panel;
    }

    /**
     * @return A JPanel containing the edit fields used to edit the article
     *         content in the article footer. This JPanel is displayed in 'Edit
     *         Mode', along with the JPanels for editing other article parts
     *         (such as the header and body).
     */
    private JPanel getFooterEditControls() {
        ArticleEditPanel panel = new ArticleEditPanel();

        // Nothing needed yet.

        return panel;
    }

    /**
     * @return A JPanel containing the edit fields used to edit the article
     *         content for this article when the user is in 'Edit Mode".
     */
    public JPanel getEditPanel() {
        JPanel panel = new JPanel();
        panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) );

        // Header edit panel.
        JPanel headerPanel = getHeaderEditControls();
        headerPanel.setAlignmentX( Component.LEFT_ALIGNMENT );
        panel.add( headerPanel );

        // Body edit panel.
        JPanel bodyPanel = getBodyEditControls();
        bodyPanel.setAlignmentX( Component.LEFT_ALIGNMENT );
        panel.add( bodyPanel );

        // Footer edit panel.
        JPanel footerPanel = getFooterEditControls();
        footerPanel.setAlignmentX( Component.LEFT_ALIGNMENT );
        panel.add( footerPanel );

        // Space all edit panels to the top.
        panel.add( Box.createVerticalGlue() );

        return panel;
    }

    /**
     * Saves the user data currently sitting in the all of the article edit
     * fields from 'Edit Mode' to this article object.
     * 
     * @return <b>True</b> if the data was successfully saved to the article
     *         object and <b>false</b> if any of the user entered data wasn't in
     *         a valid format. A message for the user about which field failed
     *         the validation check can be retrieved with a call to
     *         getInvalidFieldMsg().
     */
    public boolean saveEditData() {
        // Make sure the current fields contain
        // validate values before proceeding.
        if ( !validateEditFields() )
            return false;

        // Update article data from edit text fields.
        name = HelperUtility.getTextNullCheck( txtName );

        // Store the custom edit data unique to this
        // type of article (person, place, etc.).
        saveCustomEditData();

        // Store the new data in the database.
        storeArticle();

        return true;
    }

    /**
     * Validates the user data currently entered in the article edit fields.
     * This is called before the data is saved from the edit fields to the
     * article object itself. If validation fails, a message to the user about
     * which field failed should be stored in invalidFieldMsg, which can be
     * retried via a call to getInvalidFieldMsg().
     * 
     * @return <b>True</b> if all of the fields contain valid data and
     *         <b>false</b> if any fields contain data that must be changed
     *         before the data can be saved to the article object.
     */
    private boolean validateEditFields() {
        /* Validate any header fields here if ever needed. */

        // Validate body fields.
        if ( !validateBodyEditFields() )
            return false;

        /* Validate any footer fields here if ever needed. */

        return true;
    }

    /**
     * A method that can be used as a standard way of validating user-input text
     * that needs to be converted to an int.
     * 
     * @return <b>True</b> if the string passed in can be parsed as an int and
     *         <b>false</b> otherwise. Saves a standard message about the field
     *         in invalidFieldMsg to inform the user.
     */
    protected boolean validateInt( String value, String fieldName ) {
        try {
            Integer.parseInt( value );
        } catch ( NumberFormatException e ) {
            invalidFieldMsg = String.format( "%s must be an integer value.",
                    fieldName );
            return false;
        }

        return true;
    }
}
